package choreganizer;

import java.io.*;
import java.io.FileOutputStream;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.prefs.Preferences;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import java.util.Calendar;
import java.util.GregorianCalendar;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;

/*

	TODO LIST:
	
	- Bug in alert notification.  I think it's from editing the chore list, and refresing the table
	
	- Bug in alert notification popups.  Think it's only when a second popup is going to happen
	
	- Replace any \n's with line.separator
	
	- Put in an option menu to let the user adjust certain things
		- Look and Feel
		- Color Scheme?
		- Font sizes
		- Alert sound
	  
	- See about the L&F errors the SystemTray throws
	  (setting JFrame.setDefaultLookAndFeelDecorated and creating the JMenuBar cause it)

	- Look into making all our modal windows unmoveable
	
	- Simple help file?
	
	- Set 'Specific' as the default option instead of 'Annual'
	
	- Database importing
	
	- Go through all the error catches, and set up a better system, where critical
	  errors are reported back to the user in a dialog box
	
	- Set my XML Parsers exception catching method to my handler classes, and give the
	  handler classes the necessary methods to catch them
	  
	- Maybe do a seperate 'invokeAndWait' at end of main, so we can read in preferences
	  to set L&F before the window is displayed
	  
	- Don't display the table if there isn't an opened database
	
	- If we don't have an open database, see if the default folder exists, if not, make it
	
	- Disable appropriate buttons when a database isn't open
*/



public class Choreganizer
extends JPanel
implements MouseListener, WindowListener
{
	String separator = System.getProperty("line.separator","\n");
	
	// Default window size
	private static final int DEFWIDTH = 500;
	private static final int DEFHEIGHT = 700;

	// important globals
	private static JFrame frame;
	private static boolean firstPaint = false;
	private Preferences prefs;
	
	// GUI components
	private DateTable dateTable;
	private JLabel dateCaption;
	private JPopupMenu jpopup;
	
	// System Tray components
	private TrayIcon trayIcon;
	private SystemTray systemTray;
	private PopupMenu trayPopup;
	
	// Date info
	private static int userMonth;
	private static int userYear;
	
	// Timer to update calendar when the day changes
	private java.util.Timer checkDayTimer;
	
	// Variables to alert us when scheduled events occur
	private AlertQueue alertQueue;
	private java.util.Timer alertTimer;
	private JOptionPane alertOptionPane;
	
	// Variables for our date changing buttons popup menu
	private java.util.Timer timer;
	private static boolean popupWasShown = false;
	private final InvisButton preMonthButton = new InvisButton();
	private final InvisButton preYearButton = new InvisButton();
	private final InvisButton postMonthButton = new InvisButton();
	private final InvisButton postYearButton = new InvisButton();
	
	// Variable we use to determine if our dateTable is active
	private boolean calendarIsActive = true;

	private static final String months[] = {
		"January", "February", "March", "April", "May", 
		"June", "July", "August", "September", "October", 
		"November", "December"};
	
	
	
	
	
	
	
		
		
	
	//////////////////////////////////////////////////////
	//
	// ENTRY-POINT / FRAME SETUP
	//
	//////////////////////////////////////////////////////

	/*
	* name:		main()
	* params:	String[] args - any commands passed into the program
	* return:	void
	* desc:		Program entry-piont
	*				Set up the 
	*/
	public static void main(String[] args) 
	{
		// Make sure we have nice window decorations?
		//JFrame.setDefaultLookAndFeelDecorated( true );
		//JDialog.setDefaultLookAndFeelDecorated( true );

		// Set the Look&Feel
		/*try {
			UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
		} catch (Exception e) {
			System.err.println("Unable to load native look and feel");
		}*/
		
		
		//Create and set up the window.
		frame = new JFrame( "Choreganizer 1.0 Beta" );
		
		try {
			ImageIcon icon = null;
			URL iconURL = ClassLoader.getSystemResource("tray.png");
			if (iconURL != null) {
				icon = new ImageIcon(iconURL);
			}
			frame.setIconImage( icon.getImage() );
		} catch (Exception e) {
			System.err.println("main: can't find frame icon");
		}
		
		//Create and set up the content pane.
		Choreganizer newContentPane = new Choreganizer();

		// More frame setup
		frame.setContentPane( newContentPane );
		frame.setJMenuBar( newContentPane.initMenuBar() );
		frame.addWindowListener( newContentPane );
		frame.setDefaultCloseOperation( JFrame.DO_NOTHING_ON_CLOSE );
	}
	
	
	/*
	* name:		initFrameSize()
	* params:	void
	* return:	void
	* desc:		Sets the size of the JFrame
	*/
	public void initFrameSize()
	{
		int frameWidth = prefs.getInt("frameWidth",DEFWIDTH);
		int frameHeight = prefs.getInt("frameHeight",DEFHEIGHT);
		frame.setSize(frameWidth,frameHeight);
	}
	
	
	/*
	* name:		initFrameLocation()
	* params:	void
	* return:	void
	* desc:		Sets the location of the JFrame
	*/
	public void initFrameLocation()
	{
		int frameX = prefs.getInt("frameX",0);
		int frameY = prefs.getInt("frameY",0);
		frame.setLocation(frameX,frameY);
	}
	
	
	/*
	* name:		initMenuBar()
	* params:	none
	* return:	JMenuBar
	* desc:		Creates a JMenuBar, sets up it's JMenuItems, and sets
	*				gives those menu items actions
	*/
	public JMenuBar initMenuBar()
	{
		JMenuBar menuBar = new JMenuBar();
		JMenu menu;
		JMenuItem mi;
		
		menu = new JMenu("File");
		menuBar.add( menu );
			menu.add( new JMenuItem(new NewAction()) );
			menu.add( new JMenuItem(new OpenAction()) );
			menu.addSeparator();
			//menu.add( new JMenuItem(new ImportAction()) );
			menu.add( new JMenuItem(new ExportAction()) );
			menu.addSeparator();
			menu.add( new JMenuItem(new TrayAction()) );
			menu.addSeparator();
			menu.add( new JMenuItem(new ExitAction()) );
		
		menu = new JMenu("Edit");
		menuBar.add( menu );
			menu.add( new JMenuItem(new EditAction()) );
			menu.addSeparator();
			//menu.add( new JMenuItem("Options") );
			//menu.addSeparator();
			menu.add( new JMenuItem(new ResetAction()) );
				menu.add( makeLandFMenu(new JMenu("Look and Feel")) );
		
		menu = new JMenu("About");
		menuBar.add( menu );
			menu.add( new JMenuItem(new AboutAction()) );
		
		return menuBar;
	}
	
	
	public JMenu makeLandFMenu(JMenu menu)
	{
		UIManager.LookAndFeelInfo lfs[] = UIManager.getInstalledLookAndFeels();
		for (int k=0; k < lfs.length; k++)
		{
			String name = lfs[k].getName();
			JMenuItem lf = new JMenuItem( new LFAction(name,lfs[k].getClassName()) );
			menu.add(lf);
		}
		return menu;
	}
	
	
	class LFAction 
	extends AbstractAction
	{
		private Class lf;
		public LFAction(String name, String className)
		{
			super(name);
			try {
				lf = Class.forName(className);
			} catch (Exception e) {
				System.err.println(e.getMessage());
			}
		}
		
		public void actionPerformed( ActionEvent e )
		{
			try {
				UIManager.setLookAndFeel( (LookAndFeel)(lf.newInstance()) );
				SwingUtilities.updateComponentTreeUI( frame );
				
				// This hack works to switch from windows l&f to metal l&f
				// Metal actually sets frame.setUndecorated(true) and draws its own window
				/*frame.dispose();
				frame.setUndecorated( !frame.isUndecorated() );
				JFrame.setDefaultLookAndFeelDecorated( frame.isUndecorated() );
				frame.setVisible(true);*/
				
				new Thread(updateCalendar).start();
			} catch (Exception ex) {
				System.out.println(ex.getMessage());
			}
		}
	}
	
	
	
	
	
	
	
	
	//////////////////////////////////////////////////////
	//
	// CONSTRUCTOR / GUI SETUP / RELATED FUNCTIONS
	//
	//////////////////////////////////////////////////////
	
	/*
	* name:		Choreganizer()
	* params:	none
	* return:	Choreganizer
	* desc:		Default Constructor
	*				
	*/
	public Choreganizer()
	{
		super( new BorderLayout() );
		
		// Get current month/year
		Calendar calendar = new GregorianCalendar();
		userMonth = calendar.get( Calendar.MONTH );
		userYear = calendar.get( Calendar.YEAR );

		// Modify the default time that tooltips will appear for
		ToolTipManager ttm = ToolTipManager.sharedInstance();
		ttm.setDismissDelay(20000);
		
		// Instantiate our custom date table
		dateTable = new DateTable();
		
		// Create date-changing buttons
		jpopup = new JPopupMenu();
		initPopButton( preYearButton, "<<", 
			"<html>Click to display the previous year<br />Hold down to display a list of previous years</html>" );
		initPopButton( preMonthButton, "<",
			"<html>Click to display the previous month<br />Hold down to display a list of previous months</html>" );
		initPopButton( postMonthButton, ">",
			"<html>Click to display the next month<br />Hold down to display a list of upcoming months</html>" );
		initPopButton( postYearButton, ">>", 
			"<html>Click to display the next year<br />Hold down to display a list of upcomming years</html>");
		
		// Create panels to hold the pre/post buttons
		JPanel leftComboPanel = new JPanel( );
		JPanel rightComboPanel = new JPanel( );
		leftComboPanel.add( preYearButton );
		leftComboPanel.add( preMonthButton );
		rightComboPanel.add( postMonthButton );
		rightComboPanel.add( postYearButton );
		
		// Create a label to display the month and year
		dateCaption = new JLabel();
		dateCaption.setFont( new Font(dateCaption.getFont().getName(), Font.BOLD, 30) );
		
		// Create a panel to hold the pre/post panels, and current date label
		JPanel datePanel = new JPanel( );
		datePanel.add( leftComboPanel );
		datePanel.add( dateCaption );
		datePanel.add( rightComboPanel );
		
		// Add the date + pre/post panels
		add( datePanel, BorderLayout.NORTH );
		// Add calendar table
		add( new JScrollPane(dateTable), BorderLayout.CENTER );
		
		prefs = getPrefs();		// Create our preference file
		initFrameSize();			// Initialize the frame's size
		initFrameLocation();		// Initialize the frame's location
		initDatabase();			// Open database files, and pass their location to our dateTable

		// Create a new thread to update our table's model with data
		new Thread(updateCalendar).start();
	}
	
	
	/*
	*
	* Holds info about an alert
	*
	*/
	class AlertQueueItem
	{
		public String name;
		public Date date;
		public ScheduleTypes.AlertTypes alertType;
		
		public AlertQueueItem(String name, Date date, ScheduleTypes.AlertTypes alertType)
		{
			this.name = name;
			this.date = date;
			this.alertType = alertType;
			
			//System.out.println("New AQI: " + name + " " + date);
		}
		
		public String toString()
		{
			return date.toString();
		}
	}
	
	
	/*
	*
	* Comparator to allow our AlertQueue to
	* sort the alerts based on date
	*
	*/
	class AlertQueueItemComparator
	implements Comparator<AlertQueueItem>
	{
		/*
		* item1  <  item2: return -1
		* item1  == item2: reutrn  0
		* item1  >  item2: return  1
		*/
		public int compare(AlertQueueItem item1, AlertQueueItem item2)
		{
			//System.out.println("Comparing two alert queue items");
			
			if (item1.date.before(item2.date))
			{
				//System.out.println("  " + item1.name + " < " + item2.name);
				return -1;
			}
			else if (item1.date.equals(item2.date))
			{
				//System.out.println("  " + item1.name + " == " + item2.name);
				return 0;
			}
			else
			{
				//System.out.println("  " + item1.name + " > " + item2.name);
				return 1;
			}
		}
	}
	
	
	/*
	*
	* A priority queue we can access with a single timer
	* to dispatch alerts to the user
	*
	*/
	class AlertQueue
	extends PriorityQueue<AlertQueueItem>
	{
		public AlertQueue(int capacity, AlertQueueItemComparator comparator)
		{
			super(capacity,comparator);
		}
	}
	
	
	/*
	* Get the ChoreSchedules for the current day, and set
	* alerts for them
	*
	*/
	public void createAlertTimers(int userMonth, int userYear)
	{
		try {
			// Create calendars
			GregorianCalendar addCal = new GregorianCalendar();
			GregorianCalendar checkCal = new GregorianCalendar();
			
			// Loop through all the ChoreSchedules for the current day
			ChoreSchedule[] choreSchedules = dateTable.getCurrentDayItem( userMonth, userYear );
			for (ChoreSchedule cs: choreSchedules)
			{
				// Get all the alert event times for this item
				String[] alertTimes = cs.getTimeList();
				for (String alertTime: alertTimes)
				{
					// Figure out the actual time this event is supposed to fire
					Date alertTimeDate = new SimpleDateFormat("HH:mm").parse(alertTime);
					checkCal.setTime( alertTimeDate );
					addCal.set( Calendar.HOUR_OF_DAY, checkCal.get(Calendar.HOUR_OF_DAY) );
					addCal.set( Calendar.MINUTE, checkCal.get(Calendar.MINUTE) );
					
					// If it's a future time, add it to the queue
					if (new Date().before(addCal.getTime()))
					{
						if (alertQueue == null) 
							alertQueue = new AlertQueue(10, new AlertQueueItemComparator());
						
						AlertQueueItem aqi = new AlertQueueItem(
							cs.getChoreData().getName(),
							addCal.getTime(),
							cs.getScheduleData().getAlertType()
							);
							
						alertQueue.add(aqi);
					}
				}
			}
			
			// Call function to start scheduling timers to alert us
			queueNextAlert();

		} catch (Exception e) {
			System.err.println("Error in Choreganizer.createAlertTimers");
			System.err.println(e);
		}
	}
	
	
	/*
	*
	* Queues up timer tasks to fire for the next scheduled event to occur
	*/
	public void queueNextAlert()
	{
		// If there are queued alerts to process...
		if (alertQueue != null && alertQueue.size() > 0)
		{
			// Get the next queued item
			AlertQueueItem aqi = alertQueue.poll();
			System.out.println("Popped top item");
			
			// Make sure the timer is instantiated
			if (alertTimer == null)
				alertTimer = new java.util.Timer();
			
			// If this date is in the past, we're late on the timer
			// schedule it to occur asap
			Date now = new Date();
			if (aqi.date.before(now))
				alertTimer.schedule( new AlertTask(aqi), now );
			
			// Otherwise, schedule it to occur at it's actual time
			else
				alertTimer.schedule( new AlertTask(aqi), aqi.date );
		}
	}
	
	
	/*
	*
	* TimerTask which we will call to 
	*
	*/
	class AlertTask
	extends TimerTask
	{
		private AlertQueueItem aqi;
		
		public AlertTask(AlertQueueItem aqi)
		{
			System.out.println("AlertTask created for: " + aqi.name + " | " + aqi.date);
			this.aqi = aqi;
		}
		
		public void run()
		{
			// handle the alert notification for this item
			if (aqi.alertType == ScheduleTypes.AlertTypes.sound)
			{
				soundAlert(aqi);
			}
			else if (aqi.alertType == ScheduleTypes.AlertTypes.silentPopup)
			{
				popupAlert(aqi);
			}
			else if (aqi.alertType == ScheduleTypes.AlertTypes.soundPopup)
			{
				soundAlert(aqi);
				popupAlert(aqi);
			}
			
			// Queue up the next item and cleanup
			queueNextAlert();
			this.cancel();
			alertTimer.purge();
			aqi = null;
		}
	}
	
	
	/*
	* We should let the user specify either a wave file,
	* or the system beeps
	*/
	public void soundAlert(AlertQueueItem aqi)
	{
		Toolkit.getDefaultToolkit().beep();
		Toolkit.getDefaultToolkit().beep();
	}
	
	
	/*
	* We should check this to see if the popup is instantiated, and
	* just add to it if it is
	*
	*/
	public void popupAlert(AlertQueueItem aqi)
	{
		if (frame.isVisible())
		{
			if (alertOptionPane == null || !alertOptionPane.isVisible())
			{
				/*alertOptionPane = new JOptionPane(
					aqi.name + " chore has occured at " + aqi.date, 
					JOptionPane.OK_OPTION, 
					JOptionPane.INFORMATION_MESSAGE, 
					null, 
					new String[]{"OK"});*/
						
				alertOptionPane = new JOptionPane();
				alertOptionPane.showMessageDialog(
					frame, 
					aqi.name + " chore has occured at " + aqi.date,
					"Scheduled Event!",
					JOptionPane.INFORMATION_MESSAGE);
				
				alertOptionPane.setVisible(true);
			}
			else
			{
				StringBuilder msg = new StringBuilder();
				msg.append( (String)alertOptionPane.getMessage() );
				msg.append( separator );
				msg.append( aqi.name + " chore has occured at " + aqi.date );
				
				alertOptionPane.setMessage( msg.toString() );
			}
		}
		else
		{
			if (trayIcon != null)
			{
				trayIcon.displayMessage("Scheduled Event!", 
					aqi.name + " chore has occured at " + aqi.date, 
					TrayIcon.MessageType.INFO);
			}
		}
		
		alertOptionPane = null;
	}
	
	
	/*
	* name:		updateCalendar
	* params:	none
	* return:	void
	* desc:		Creates a seperate thread which will tell our DateTable to flush its
	*				table, and to display the calendar and chores for the passed in time.
	*				We then redisplay our GUI elements in the event-dispatch thread
	*/
	Runnable updateCalendar = new Runnable()
	{
		public void run()
		{
			GregorianCalendar calendar = new GregorianCalendar();
			
			// Have our model create a tabled based on the input date and have it label any
			// chores set for the days of the month
			dateTable.initialize( userMonth, userYear );
			
			// Remove any alert timers
			if (alertTimer != null)
			{
				alertTimer.cancel();
				alertTimer.purge();
				alertTimer = null;
			}
			
			// Remove any update timers
			if (checkDayTimer != null)
			{
				checkDayTimer.cancel();
				checkDayTimer.purge();
				checkDayTimer = null;
			}
			
			// Determine if the calendar is in active mode or not
			if (userMonth == calendar.get(Calendar.MONTH) && userYear == calendar.get(Calendar.YEAR) )
			{
				calendarIsActive = true;
				
				if (checkDayTimer == null)
				{
					checkDayTimer = new java.util.Timer();
				}
				
				// Morning of the next day
				calendar.set(
					calendar.get(Calendar.YEAR), 
					calendar.get(Calendar.MONTH), 
					calendar.get(Calendar.DAY_OF_MONTH)+1,
					0,
					0);
					
				// Set a timer to refresh the page when it becomes a new day
				checkDayTimer.schedule(new UpdateTask(), calendar.getTime());
				
				// Create timers to notify us of any scheduled events which occur
				// for the current month/year/day
				createAlertTimers(userMonth, userYear);
			}
			else
			{
				calendarIsActive = false;
				
				/*// Remove any update timers
				if (checkDayTimer != null)
				{
					checkDayTimer.cancel();
					checkDayTimer.purge();
					checkDayTimer = null;
				}*/
				
				/*// Remove any alert timers
				if (alertTimer != null)
				{
					alertTimer.cancel();
					alertTimer.purge();
					alertTimer = null;
				}*/
			}
			
			
			// Execute UI updates on event-dispatch thread
			//SwingUtilities.invokeLater(
			try {
			SwingUtilities.invokeAndWait(
				new Runnable()
				{
					// Update UI
					public void run()
					{
						// Set the date display
						dateCaption.setText( months[userMonth] + " " + String.valueOf(userYear) );
						
						if (!calendarIsActive)
						{
							//dateCaption.setForeground(Color.RED);
							dateCaption.setEnabled(false);
							dateCaption.setToolTipText(
								"<html>A previous date<br />" +
								"You will not be notified of any scheduled events that<br />" +
								"occur until you return to viewing the current date</html>");
						}
						else
						{
							//dateCaption.setForeground(Color.BLACK);
							dateCaption.setEnabled(true);
							dateCaption.setToolTipText("<html>The current date</html>");
						}

						// Cancel any cell editing happening, so the cell editor isn't open, displaying
						// old data after we modify the tables model
						if ( dateTable.isEditing() )
						{
							DateTableCellEditor editor = (DateTableCellEditor)dateTable.getCellEditor();
							editor.cancelCellEditing();
							editor = null;
						}
						
						// Alert listeners.  This fixes the border redraw problem.
						((DateTableModel)dateTable.getModel()).fireTableStructureChanged();
						
						
						// Easy hack to keep the frame from displaying at startup until
						// the calendar's model has been built
						if (!firstPaint)
						{
							firstPaint = true;
							frame.setVisible(true);
						}
						
						// Tell frame and it's children to repaint
						frame.repaint();
						frame.validate();
					}
				});
			} catch (Exception e) {
				System.err.println("Error in Choreganizer.updateCalendar()");
				System.err.println(e);
			}
				
			calendar = null;
		}
	};
	
	
	/*
	* timer task we can call at a specific date
	* to update the calendar
	*/
	class UpdateTask extends TimerTask 
	{
		public UpdateTask()
		{
		}
		
		public void run()
		{
			checkDayTimer.cancel();
			checkDayTimer.purge();
			checkDayTimer = null;
			
			new Thread(updateCalendar).start();
		}
	}
	
	
	/*
	* Name:		initPopButton()
	* Params:	JButton mb - JButton to setup
	*				String txt - JButtons text
	*				String tooltip - JButtons tooltip
	* Return:	void
	* Desc:		Used to setup JButtons which we will use to later to trigger
	*				time delayed JPopupMenu's
	*/
	public void initPopButton(InvisButton mb, String txt, String tooltip)
	{
		Font lFont = new Font( mb.getFont().getName(), Font.BOLD, 20 );
		
		mb.setFont(lFont);
		mb.setText(txt);
		mb.setToolTipText(tooltip);
		mb.addMouseListener(this);
	}
	
	
	/*
	* class:		SetPopupVis
	* desc:		A timer which is set to display the JPopupMenu after 3 seconds
	*				This is called from the buttons on the date change panel, and
	*				can be canceled if the mouse is released before the 3 seconds
	*				are up
	*/
	class SetPopupVis extends TimerTask 
	{
		private JButton button;
		
		public SetPopupVis( JButton jb )
		{
			button = jb;
		}
		
		public void run()
		{
			// Have the popup display in the EventDispatch thread
			SwingUtilities.invokeLater(new Runnable() { public void run()	
			{
				jpopup.show( button,0,0+button.getSize().height );
			}});

			timer.cancel();
			timer.purge();
			popupWasShown = true;
		}
		
	}
	
	
	/*
	* name:		inivializeDatabase()
	* params:	none
	* return:	void
	* desc:		Opens our database file
	*/
	public void initDatabase()
	{
		// Get the location of our database file
		File databaseFile = new File( prefs.get("databaseJar","") );
		// Pass the database file into our table model
		dateTable.setDatabase( databaseFile );
		// Cleanup
		databaseFile = null;
	}
	
	
	/*
	* name:		getPrefs()
	* params:	none
	* return:	Preferences
	* desc:		Returns the users preference file associated with out program
	*				If one doesn't exist, we'll create a default one
	*/
	public Preferences getPrefs()
	{
		// Get [create if needed] our preference file
		Preferences preference = Preferences.userNodeForPackage( this.getClass() );
		// Check to see if the preference file is new
		boolean exist = preference.getBoolean("created",false);
		
		// If it's a new preferenc file..
		if (!exist)
		{
			// Fill the prefrence file w/ our default values
			preference = reset(preference);
		}

		return preference;
	}
	
	
	/*
	* name:		createFile()
	* params:	File f - the file to create
	*				boolean overwrite - if we should overwrite an existing file
	* return:	void
	* desc:		Creates the input file, generating any new directories it
	*				needs to, and can overwrite an existing file based on input params
	*/
	public void createFile(File f, boolean overwrite)
	{
		try {
			// Create any necessary parent directories
			String parent = f.getParent();
			if ( parent != null && !f.equals("") )
			{
				File dir = new File( parent );
				if ( dir.isDirectory() && !dir.exists() )
					dir.mkdirs();
				
				dir = null;
			}
			
			// If we want to overwrite the existing file, we must delete the old one
			if ( f.exists() && overwrite )
				f.delete();
			// Create the file
			f.createNewFile();
			
			f = null;
			parent = null;
		} catch (Exception e) {
			System.err.println("Unable to create file: " + f);
		}
	}
	
	
	/*
	*
	* creates a dummy database zip file w/ dummy xml entries
	*/
	public void createDatabaseFile(File f, boolean saveLoc)
	{
		// See if we should save the file location
		if (saveLoc)
			prefs.put("databaseJar",f.toString());

		FileOutputStream fos = null;
		ZipOutputStream out = null;
		try {
			// Create the ZIP file
			fos = new FileOutputStream(f);
			out = new ZipOutputStream(fos);
			
			// Put in dummy files
			out.putNextEntry(new ZipEntry("chores.xml"));
			out.closeEntry();
			
			out.putNextEntry(new ZipEntry("schedules.xml"));
			out.closeEntry();
		} catch (IOException e) {
			System.err.println(e);
		} finally {
			try {
				// We MUST close the ZipOutputStream first
				if (out != null)
				{
					out.close();
					out = null;
				}
				// Now we can close the FileOutputStream
				if (fos != null)
				{
					fos.close();
					fos = null;
				}
			} catch (Exception e) {
				// shouldn't have to worry about this
			}
		}
	}

	
	
	
	
	
	
	//////////////////////////////////////////////////////
	//
	// ACTION FUNCTIONS
	//
	//////////////////////////////////////////////////////
	
	/*
	* open a database file, save it's location
	*
	*/
	public void openDatabase()
	{
		// Create FileChooser
		JFileChooser opener = new JFileChooser();
		DBFileFilter filter = new DBFileFilter();
		opener.setFileFilter(filter);
		opener.setDialogTitle("Select a chore schedule database");
		opener.setDialogType(JFileChooser.OPEN_DIALOG);
		
		// Get last database location
		File lastLoc = new File( prefs.get("databaseJar","") );
		if (lastLoc.toString().equals("") || !lastLoc.exists())
		{
			lastLoc = new File( prefs.get("programHome","") );
		}
		opener.setCurrentDirectory(lastLoc);
		
		// If they didn't cancel...
		int returnVal = opener.showDialog( opener, "Open" );
		if (returnVal == 0)
		{
			File databaseFile = opener.getSelectedFile();
			
			// Put the selected file into our prefs
			prefs.put("databaseJar",databaseFile.toString());

			// Pass the database files into our table model
			dateTable.setDatabase( databaseFile );
			initDatabase();
			
			new Thread(updateCalendar).start();
			
			databaseFile = null;
		}
		// Cleanup
		opener.setVisible(false);
		opener = null;
		filter = null;
		lastLoc = null;
	}
	
	
	/*
	* Create a new database file, save it's location
	* don't allow it to overwrite a currently open database
	*
	*/
	public void newDatabase()
	{
		// Create FileChooser
		JFileChooser opener = new JFileChooser();
		DBFileFilter filter = new DBFileFilter();
		opener.setFileFilter(filter);
		opener.setDialogTitle("Create a chore schedule database");
		opener.setDialogType(JFileChooser.SAVE_DIALOG);
		
		// Get last database location
		File lastLoc = new File( prefs.get("databaseJar","") );
		if (lastLoc.toString().equals("") || !lastLoc.exists())
		{
			lastLoc = new File( prefs.get("programHome","") );
		}
		opener.setCurrentDirectory(lastLoc);
		
		// If they didn't cancel...
		int returnVal = opener.showDialog( opener, "Create" );
		if (returnVal == 0)
		{
			// Make sure the file has the extension
			File databaseFile = opener.getSelectedFile();
			if ( !databaseFile.toString().endsWith(filter.getExtension()) )
			{
				databaseFile = new File( databaseFile.toString() + "." + filter.getExtension() );
			}
			
			opener.setVisible(false);
			
			
			// If file exists, ask if they want to overwrite
			if (databaseFile.exists())
			{
				File currentDatabasFile = new File( prefs.get("databaseJar","") );
				// It's the database we're using right now, don't allow it
				if ( databaseFile.getAbsolutePath().equals(currentDatabasFile.getAbsolutePath()) )
				{
					 JOptionPane.showMessageDialog(frame, 
					 	"You're attempting to overwrite the currently used database\n" +
						"Please pick another file location", 
						"Warning!", JOptionPane.ERROR_MESSAGE);
						
					newDatabase();
				}
				else
				{
					// Query the user if they really want to make a new project
					JOptionPane pane = new JOptionPane();
					int selectedOption = pane.showConfirmDialog(
						frame, "Overwrite the current file?", "File already exists",
						JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
						
					// They want to overwrite
					if (selectedOption == JOptionPane.YES_OPTION)
					{
						// Create the Jar (if it doesn't exist);
						createDatabaseFile( databaseFile, true );
						initDatabase();
						new Thread(updateCalendar).start();
					}
					// They don't want to overwrite, let them pick again
					else if (selectedOption == JOptionPane.NO_OPTION)
					{
						newDatabase();
					}
					
					pane = null;
				}
			}
			
			// File doesn't exist, create it
			else
			{
				// Create the Jar (if it doesn't exist);
				createDatabaseFile( databaseFile, true );
				initDatabase();
				new Thread(updateCalendar).start();
			}
			
			databaseFile = null;
		}
		
		// Cleanup
		opener = null;
		filter = null;
	}
	
	
	/*
	* Import a database's chore schedules into our
	* current database file
	*
	*/
	public void importDatabase()
	{
	}
	
	
	/*
	* Export the current database to another location
	* Don't allow it to overwrite a currently open database
	*
	*/
	public void exportDatabase()
	{
		JFileChooser opener = new JFileChooser();
		DBFileFilter filter = new DBFileFilter();
		opener.setFileFilter(filter);
		opener.setDialogTitle("Export your chore schedule database");
		opener.setDialogType(JFileChooser.SAVE_DIALOG);
		
		// Get last database location
		File lastLoc = new File( prefs.get("databaseJar","") );
		if (lastLoc.toString().equals("") || !lastLoc.exists())
		{
			lastLoc = new File( prefs.get("programHome","") );
		}
		opener.setCurrentDirectory(lastLoc);
		
		// If they didn't cancel...
		int returnVal = opener.showDialog( opener, "Save" );
		if (returnVal == 0)
		{
			// Make sure the file has the extension
			File exportFile = opener.getSelectedFile();
			if ( !exportFile.toString().endsWith(filter.getExtension()) )
			{
				exportFile = new File( exportFile.toString() + "." + filter.getExtension() );
			}
			
			opener.setVisible(false);
			
			
			// If file exists, ask if they want to overwrite
			if (exportFile.exists())
			{
				File databaseFile = new File( prefs.get("databaseJar","") );
				
				// It's the database we're using right now, don't allow it
				if ( databaseFile.getAbsolutePath().equals(exportFile.getAbsolutePath()) )
				{
					 JOptionPane.showMessageDialog(frame, 
					 	"You're attempting to overwrite the currently used database\n" +
						"Please pick another file location", 
						"Warning!", JOptionPane.ERROR_MESSAGE);
						
					exportDatabase();
				}
				
				// It's some other existing file
				else
				{
					// Query the user if they really want to make a new project
					JOptionPane pane = new JOptionPane();
					int selectedOption = pane.showConfirmDialog(
						frame, "Overwrite the current file?", "File already exists",
						JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
						
					// They want to overwrite
					if (selectedOption == JOptionPane.YES_OPTION)
					{
						createDatabaseFile( exportFile, false );
						
						// Create an instance of our animated glass pane
						SavingProgressGlassPane glassPane = 
							new SavingProgressGlassPane("Exporting Database");
						// Set this as our frames glass pane
						frame.setGlassPane(glassPane);
						// Start the animation
						glassPane.start();
				
						MyReaders reader = new MyReaders( databaseFile );
						MyWriters writer = new MyWriters( exportFile );
						ChoreData[] chores = reader.getAllChores();
						ScheduleData[] schedules = reader.getAllSchedules();
						writer.writeToDatabase(chores,schedules);
						
						glassPane.stop();
						glassPane = null;
						
						reader = null;
						writer = null;
						chores = null;
						schedules = null;
					}
					
					// They don't want to overwrite, let them pick again
					else if (selectedOption == JOptionPane.NO_OPTION)
					{
						exportDatabase();
					}
					
					pane = null;
				}
			}
			
			// File doesn't exist, create it
			else
			{
				// Create the Jar (if it doesn't exist);
				createDatabaseFile( exportFile, false );
				
				// Create an instance of our animated glass pane
				SavingProgressGlassPane glassPane = 
					new SavingProgressGlassPane("Exporting Database");
				// Set this as our frames glass pane
				frame.setGlassPane(glassPane);
				// Start the animation
				glassPane.start();
				
				File databaseFile = new File( prefs.get("databaseJar","") );
				MyReaders reader = new MyReaders( databaseFile );
				MyWriters writer = new MyWriters( exportFile );
				ChoreData[] chores = reader.getAllChores();
				ScheduleData[] schedules = reader.getAllSchedules();
				writer.writeToDatabase(chores,schedules);
				
				glassPane.stop();
				glassPane = null;
				
				reader = null;
				writer = null;
				chores = null;
				schedules = null;
			}
			
			exportFile = null;
		}
		
		// Cleanup
		opener = null;
		filter = null;
	}
				
	
	/*
	* name:		exit()
	* params:	none
	* return:	void
	* desc:		Asks the user if they want to exit the program
	*/
	public void exit()
	{
		// Query the user to save
		/*JOptionPane pane = new JOptionPane();
		int exitCheck = pane.showConfirmDialog(frame, "Are you sure you wish to exit the program?", 
							 "Exit?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);

		if (exitCheck == JOptionPane.YES_OPTION)
		{
			frame.setVisible(false);
			frame.dispose();
			System.exit(0);
		}
		
		pane = null;*/
		
		if (prefs != null)
		{
			prefs.putInt("frameX",frame.getX());
			prefs.putInt("frameY",frame.getY());
			prefs.putInt("frameWidth",frame.getSize().width);
			prefs.putInt("frameHeight",frame.getSize().height);
		}
		
		System.exit(0);
	}
	
	
	/*
	* name:		hideToTray()
	* params:	none
	* return:	void
	* desc:		If possible, creates a SystemTray object, creates an icon to put in
	*				the tray, and then hides our program so only the system tray is
	*				visible (thus, our program is essentially running in the background)
	*/
	public void hideToTray()
	{
		if (SystemTray.isSupported()) 
		{
			systemTray = SystemTray.getSystemTray();
			
			ImageIcon icon = null;
			try {
				URL iconURL = ClassLoader.getSystemResource("tray.png");
				icon = new ImageIcon(iconURL);
			} catch (Exception e) {
				System.err.println("Choreganizer.hideToTray()");
				System.err.println(e);
			}
			
			ActionListener exitListener = new ActionListener() 
			{
				 public void actionPerformed(ActionEvent e) 
				 {
					  exit();
				 }
			};
			
			ActionListener showListener = new ActionListener() 
			{
				 public void actionPerformed(ActionEvent e) 
				 {
					  frame.setVisible(true);
					  // Hopefully fixes reports of glasspane being visible, and capturing
					  // mouse events when they unhide the program.
					  frame.getGlassPane().setVisible(false);
					  for (TrayIcon ti: systemTray.getTrayIcons())
					  {
						  systemTray.remove(ti);
					  }
					  trayPopup = null;
					  trayIcon = null;
					  systemTray = null;
				 }
			};
			
			trayPopup = new PopupMenu();
			MenuItem exitItem = new MenuItem( "Exit" );
			MenuItem showItem = new MenuItem( "Show" );
			exitItem.addActionListener(exitListener);
			showItem.addActionListener(showListener);
			trayPopup.add(showItem);
			trayPopup.add(exitItem);

			trayIcon = new TrayIcon(icon.getImage(), "Choreganizer", trayPopup);
			trayIcon.setImageAutoSize(true);
			icon = null;
			
			try {
				 systemTray.add(trayIcon);
			} catch (AWTException awte) {
				System.err.println(awte);
			}
			
			frame.setVisible(false);
			frame.dispose();
			System.gc();
		} else {
			System.err.println("System tray is currently not supported.");
		}
	}
	
	
	/*
	* name:		reset
	* params:	preference - Preference file to reset
	* return:	Preferences
	* desc:		Clears the preference file, and restores the programs default settings
	*/
	public Preferences reset(Preferences preference)
	{
		// Save the database location, if it exists
		final String databaseJar = preference.get("databaseJar","");
		
		// Instead of just clearing the preference file, we'll completely
		// wipe it and start over
		try {
			//preference.clear();
			preference.removeNode();
			preference = Preferences.userNodeForPackage( this.getClass() );
		} catch (Exception e) {
			System.err.println(e.getMessage());
		}
		
		Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();

		// Reset the preference file
		preference.put("userHome",System.getProperty("user.home","."));
		preference.put("programHome",System.getProperty("user.home",".") + File.separator + "choreganizer" + File.separator);
		preference.putInt("frameWidth",DEFHEIGHT);
		preference.putInt("frameHeight",DEFWIDTH);
		preference.putInt("frameX",(screen.width - DEFHEIGHT)/2);
		preference.putInt("frameY",(screen.height - DEFWIDTH)/2);
		preference.putBoolean("created",true);
		preference.putBoolean("CrossOutOldDays",true);
		preference.put("databaseJar",databaseJar);
		
		// Reset to current date
		GregorianCalendar calendar = new GregorianCalendar();
		userMonth = calendar.get( Calendar.MONTH );
		userYear = calendar.get( Calendar.YEAR );
		
		calendar = null;
		screen = null;
		
		return preference;
	}
	
	
	
	
	
	
	
	
	
	
	//////////////////////////////////////////////////////
	//
	// LISTENERS
	//
	//////////////////////////////////////////////////////

	/*
	* hideToTray when the frame is closed
	*
	*/
	public void windowClosing(WindowEvent e) 
	{ 
		if (e.getSource() instanceof JFrame)
		{
			hideToTray(); 
			return;
		}
	}
	
	/*
	* display wait panel when we're writing to database
	*
	*/
	public void windowClosed(WindowEvent e) 
	{
		// EditDialog is closing
		if (e.getSource() instanceof EditDialog)
		{
			EditDialog ed = (EditDialog)e.getSource();
			
			// Remove the window listener on this object
			ed.removeWindowListener(Choreganizer.this);
			
			// We've modified the database
			if (ed.needToUpdate())
			{
				// Create an instance of our animated glass pane
				SavingProgressGlassPane glassPane = 
					new SavingProgressGlassPane("Saving Database");
				// Set this as our frames glass pane
				frame.setGlassPane(glassPane);
				// Start the animation
				glassPane.start();
				
				// Write out our database.  We do this in the main thread, while the
				// glass pane animates in a background thread
				Preferences prefs = Preferences.userNodeForPackage( this.getClass() );
				File databaseFile = new File( prefs.get("databaseJar","") );
				MyWriters writer = new MyWriters( databaseFile );
				ChoreData[] chores = ed.getChores();
				ScheduleData[] schedules = ed.getSchedules();
				writer.writeToDatabase(chores,schedules);
				
				// Tell the glass pane it can close
				glassPane.stop();
				glassPane = null;
				
				chores = null;
				schedules = null;
				writer = null;
				databaseFile = null;
				
				// Re-parse and display the database
				new Thread(updateCalendar).start();
			}
			
			ed.setVisible(false);
			ed.dispose();
			ed = null;
		}
	}
	
	public void windowActivated(WindowEvent e) 
	{
	}
	
	public void windowDeactivated(WindowEvent e) 
	{
	}
	
	public void windowDeiconified(WindowEvent e) 
	{
	}
	
	public void windowIconified(WindowEvent e) {
	}
	
	public void windowOpened(WindowEvent e) 
	{
	}
	
	
	/*
	* These mouse listeners are attached to the buttons by the date label, which
	* allos us to modify the display date and calendar table.  We also populate
	* our JPopupMenu that appears when the mouse is held down
	*/
	public void mouseClicked( MouseEvent e )
	{
	}
	
	public void mouseEntered( MouseEvent e )
	{
		// We know the event should come from a JButton
		final JButton button = (JButton)e.getSource();
		
		// Paint the border to signify the mouseOver (we disabled it on creation)
		button.setBorderPainted(true);
		// Creating the popup can keep buttons from releasing, so set it to false here
		button.getModel().setPressed(false);
		
		// Now, based on which button was pressed, modify the popup's data
		jpopup.removeAll();
		if ( button == preYearButton )
		{
			for (int i=1; i < 6; i++)
				jpopup.add( new YearAction(String.valueOf(userYear-i),userYear-i) );
		}
		else if ( button == preMonthButton )
		{
			int x = userMonth;
			for (int i=1; i < 6; i++)
			{
				x -= 1;
				if (x < 0)
					x = 11;
				
				jpopup.add( new MonthAction(months[x]) );
			}
		}
		else if ( button == postMonthButton )
		{
			int x = userMonth;
			for (int i=1; i < 6; i++)
			{
				x += 1;
				if (x > 11)
					x = 0;

				jpopup.add( new MonthAction(months[x]) );
			}
		}
		else if ( button == postYearButton )
		{
			for (int i=1; i < 6; i++)
				jpopup.add( new YearAction(String.valueOf(userYear+i),userYear+i) );
		}
		
		// If the popup was visible, we've just changed it's data to the JButton we're over now
		// So, we'll move the popup to this button
		if ( jpopup.isVisible() )
		{
			SwingUtilities.invokeLater(new Runnable() { public void run()	
			{
				jpopup.show( button,0,0+button.getSize().height );
			}});
		}
	}
	
	public void mouseExited( MouseEvent e )
	{
		// Reset the JButtons border to unpained
		JButton button = (JButton)e.getSource();
		button.setBorderPainted(false);
		button = null;
	}
	
	public void mousePressed( MouseEvent e )
	{
		JButton button = (JButton)e.getSource();
		
		// Create a timer to show the popup menu in one second, if not canceled beforehand
		popupWasShown = false;
		timer = new java.util.Timer();
		timer.schedule( new SetPopupVis(button), 1*1000 );
		
		button = null;
	}
	
	public void mouseReleased( MouseEvent e )
	{
		JButton button = (JButton)e.getSource();
		
		// If the popup window isn't visible, act like a single click, and adjust the date
		if ( !popupWasShown )
		{
			if ( button == preYearButton )
			{
				userYear -= 1;
			}
			else if ( button == preMonthButton )
			{
				userMonth -= 1;
				if (userMonth < 0)
					userMonth = 11;
			}
			else if ( button == postMonthButton )
			{
				userMonth += 1;
				if (userMonth > 11)
					userMonth = 0;
			}
			else if ( button == postYearButton )
			{
				userYear += 1;
			}
			new Thread(updateCalendar).start();
		}

		// Cancel any timer that exists to show the popup menu
		timer.cancel();
		popupWasShown = false;
		
		button = null;
	}
	
	
	
	
	
	
	
	
	
	
	//////////////////////////////////////////////////////
	//
	// ACTIONS
	//
	//////////////////////////////////////////////////////
	
	/*
	*
	*
	*/
	class OpenAction
	extends AbstractAction
	{
		public OpenAction()
		{
			super("Open Database");
			putValue(AbstractAction.SHORT_DESCRIPTION,
				"Opens a chore schedule database file");
		}
		
		public void actionPerformed(ActionEvent e)
		{
			openDatabase();
		}
	}
	
	
	/*
	*
	*
	*/
	class NewAction
	extends AbstractAction
	{
		public NewAction()
		{
			super("New Database");
			putValue(AbstractAction.SHORT_DESCRIPTION,
				"Creates a new chore schedule database file");
		}
		
		public void actionPerformed(ActionEvent e)
		{
			newDatabase();
		}
	}
	
	
	/*
	*
	*
	*/
	class ImportAction
	extends AbstractAction
	{
		public ImportAction()
		{
			super("Import Database");
			putValue(AbstractAction.SHORT_DESCRIPTION,
				"Imports a chore schedule database into your current database file");
		}
		
		public void actionPerformed(ActionEvent e)
		{
			importDatabase();
		}
	}
	
	
	/*
	*
	*
	*/
	class ExportAction
	extends AbstractAction
	{
		public ExportAction()
		{
			super("Export Database");
			putValue(AbstractAction.SHORT_DESCRIPTION,
				"Exports your current chore schedule database file");
		}
		
		public void actionPerformed(ActionEvent e)
		{
			exportDatabase();
		}
	}
	
	
	/*
	* class:	YearAction
	* desc:	Action called when the JPopupMenu is used to modify
	* 			the dates Year
	*/
	class YearAction extends AbstractAction
	{
		private int year;
		public YearAction( String t, int y )
		{
			super( t );
			year = y;
		}
		public void actionPerformed( ActionEvent e )
		{
			userYear = year;
			new Thread(updateCalendar).start();
		}
	}
	
	
	/*
	* class:	MonthAction
	* desc:	Action called when the JPopupMenu is used to modify
	* 			the dates Month
	*/
	class MonthAction extends AbstractAction
	{
		private String monthName;
		public MonthAction( String t )
		{
			super( t );
			monthName = t;
		}
		public void actionPerformed( ActionEvent e )
		{
			for (int i=0; i < months.length; i++)
				if ( months[i].equals(monthName))
				{
					userMonth = i;
					new Thread(updateCalendar).start();
				}
		}
	}
	
	
	/*
	* class:	ExitAction
	* desc:	Action called when the user asks to exit the program,
	* 			or when the window's border close icon is clicked
	*/
	class ExitAction extends AbstractAction
	{
		public ExitAction()
		{
			super("Exit");
			putValue(AbstractAction.SHORT_DESCRIPTION,
				"Exit the program");
		}
		public void actionPerformed( ActionEvent e )
		{
			exit();
		}
	}
	
	
	/*
	* class:	ResetAction
	* desc:	Resets our preferences to their default values
	*/
	class ResetAction extends AbstractAction
	{
		public ResetAction()
		{
			super("Restore Defaults");
			putValue(AbstractAction.SHORT_DESCRIPTION,
				"Restore the program's default settings");
		}
		public void actionPerformed( ActionEvent e )
		{
			// Reset our preferences
			prefs = reset(prefs);
			
			// Change window
			SwingUtilities.invokeLater(new Runnable() { public void run()
			{
				initFrameSize();
				initFrameLocation();
			}});
			
			// Update display
			new Thread(updateCalendar).start();
		}
	}
	
	
	/*
	* class:	AboutAction
	* desc:	Action called to display the About dialog box
	*/
	class AboutAction extends AbstractAction
	{
		public AboutAction()
		{
			super("About Choreganizer");
			putValue(AbstractAction.SHORT_DESCRIPTION,
				"About Choreganizer");
		}
		public void actionPerformed( ActionEvent e )
		{
			AboutDialog ad = new AboutDialog( frame );
		}
	}
	
	
	/*
	* class:	TrayAction
	* desc:	Action used to create a TrayIcon, and to hide the
	* 			the main window
	*/
	class TrayAction extends AbstractAction
	{
		public TrayAction()
		{
			super("Minimize to Tray");
			putValue(AbstractAction.SHORT_DESCRIPTION,
				"Minimizes it to the System's Tray to run in the background");
		}
		
		public void actionPerformed( ActionEvent e )
		{
			hideToTray();
		}
	}
	
	
	// MAKE A RUNNABLE OBJECT, PUT THIS CODE IN IT, AND START 
	// A THREAD FROM IT
	/*
	* class:	EditAction
	* desc:	Action used to open the dialog panel which allows
	*			us to edit our chores/schedules
	*/
	class EditAction extends AbstractAction
	{
		public EditAction()
		{
			super("Edit Chores & Schedules");
			putValue(AbstractAction.SHORT_DESCRIPTION,
				"Modify your chore schedule database");
		}
		
		public void actionPerformed( ActionEvent e )
		{
			File databaseFile = new File( prefs.get("databaseJar","") );
			
			MyReaders reader = new MyReaders( databaseFile );
			
			// Get a complete list of all chores/schedules
			ChoreData[] chores = reader.getAllChores();
			ScheduleData[] schedules = reader.getAllSchedules();
			
			// Open the editor up, and attach a window listener on it so
			// we know if/when to write
			EditDialog editor = new EditDialog(frame,chores,schedules);
			editor.addWindowListener(Choreganizer.this);
			
			// Cleanup
			databaseFile = null;
			reader = null;
			chores = null;
			schedules = null;
		}
	}
	
	
	
	
	
	
}



/*
* Simple hack to keep the button as non-opaque, even when the 
* L&F has been changed while the program is running
*/
class InvisButton
extends JButton
{
	public InvisButton()
	{
		super();
		setUI( new BasicButtonUI() );
		setFocusPainted(false);
		setBorderPainted(false);
		setMargin( new Insets(10,10,10,10) );
	}
	
	public void updateUI()
	{
		super.updateUI();
		setUI( new BasicButtonUI() );
	}
}


